/**
 * @file TelinkUart.c
 * @brief 
 * @author xuesong (wenshaoliu@foxmail.com)
 * @version 1.0
 * @date 2024-10-09
 * 
 * @copyright Copyright (c) 2024  by xuesong
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-10-09     <td>1.0     <td>xuesong   <td>内容
 * </table>
 */
#include "TelinkUart.h"

/**
 * @brief     This function is used to look for the prime.if the prime is finded,it will
 * 			  return 1, or return 0.
 * @param[in] the value to judge
 * @return    none
 */
static unsigned char IsPrime(unsigned int n)
{
    unsigned int i = 5;
    if (n <= 3)
    {
        return 1; // althought n is prime, but the bwpc must be larger than 2.
    }
    else if ((n % 2 == 0) || (n % 3 == 0))
    {
        return 0;
    }
    else
    {
        for (i = 5; i * i < n; i += 6)
        {
            if ((n % i == 0) || (n % (i + 2)) == 0)
            {
                return 0;
            }
        }
        return 1;
    }
}

/**
 * @brief  		This funciton serves to calculate the best bwpc(bit width) .i.e reg0x96
 * @param[in] 	baut_rate:The value of the baut rate to set.
 * @param[in] 	tmp_sysclk:The system clock
 *    			algorithm: BaudRate*(div+1)*(bwpc+1)=system clock
 *    			simplify the expression: div*bwpc =  constant(z)
 *    			bwpc range from 3 to 15.so loop and
 *    			get the minimum one decimal point
 * @return 		the position of getting the minimum value
 */
static unsigned char g_bwpc = 0;
static unsigned int g_uart_div = 0;
void GetBetterBwpc(unsigned int baut_rate, unsigned int tmp_sysclk)
{
    unsigned char i = 0, j = 0;
    unsigned int primeInt = 0;
    unsigned char primeDec = 0;
    unsigned int D_intdec[13], D_int[13];
    unsigned char D_dec[13];

    primeInt = tmp_sysclk / baut_rate;
    primeDec = 10 * tmp_sysclk / baut_rate - 10 * primeInt;
    /************************************************************
     * calculate the primeInt and check whether primeInt is prime.
     */
    if (IsPrime(primeInt))
    {                  // primeInt is prime
        primeInt += 1; //+1 must be not prime. and primeInt must be larger than 2.
    }
    else
    {
        if (primeDec > 5)
        { // >5
            primeInt += 1;
            if (IsPrime(primeInt))
            {
                primeInt -= 1;
            }
        }
    }
    /*******************************************
     * get the best division value and bit width
     */
    for (i = 3; i <= 15; i++)
    {
        D_intdec[i - 3] = (10 * primeInt) / (i + 1);                  ////get the LSB
        D_dec[i - 3] = D_intdec[i - 3] - 10 * (D_intdec[i - 3] / 10); /// get the decimal section
        D_int[i - 3] = D_intdec[i - 3] / 10;                          /// get the integer section
    }

    // find the max and min one decimation point
    unsigned char position_min = 0, position_max = 0;
    unsigned int min = 0xffffffff, max = 0x00;
    for (j = 0; j < 13; j++)
    {
        if ((D_dec[j] <= min) && (D_int[j] != 0x01))
        {
            min = D_dec[j];
            position_min = j;
        }
        if (D_dec[j] >= max)
        {
            max = D_dec[j];
            position_max = j;
        }
    }

    if ((D_dec[position_min] < 5) && (D_dec[position_max] >= 5))
    {
        if (D_dec[position_min] < (10 - D_dec[position_max]))
        {
            g_bwpc = position_min + 3;
            g_uart_div = D_int[position_min] - 1;
        }
        else
        {
            g_bwpc = position_max + 3;
            g_uart_div = D_int[position_max];
        }
    }
    else if ((D_dec[position_min] < 5) && (D_dec[position_max] < 5))
    {
        g_bwpc = position_min + 3;
        g_uart_div = D_int[position_min] - 1;
    }
    else
    {
        g_bwpc = position_max + 3;
        g_uart_div = D_int[position_max];
    }
}

void MainGetBetterBwpc(unsigned int baut_rate, unsigned int tmp_sysclk)
{
    GetBetterBwpc(baut_rate, tmp_sysclk);
    printf("g_uart_div =%d,g_bwpc= %d\r\n", g_uart_div, g_bwpc);
}
